/*
 * DTDComplexRule.cpp
 *
 *  Created on: 29 mars 2011
 *      Author: gdoussin
 */

#include <vector>
#include <iostream>
#include <algorithm>

#include "DTDComplexRule.h"

using namespace std;

DTDComplexRule::DTDComplexRule(type joinType):DTDRule()
{
	this->joinType = joinType;
}

void DTDComplexRule::addRule(DTDRule *rule)
{
	this->rules.push_back(rule);
}

void DTDComplexRule::addRuleFront(DTDRule *rule)
{
	vector<DTDRule *>::iterator it;

	it = rules.begin();
	rules.insert(it, rule);
}

void DTDComplexRule::setRules(vector<DTDRule *> rules)
{
	this->rules = rules;
}

bool DTDComplexRule::isValid(vector<string> balises)
{
	type typeActuel = this->joinType;
	vector< string >::iterator itBalises;
	vector< string > nextBalises; // Liste des balises pouvant se trouver directement après la balise actuelle

	for(itBalises = balises.begin(); itBalises != balises.end(); itBalises++)
	{
		// Si la balise ne fait pas partie de la liste des balises suivantes, on renvoie faux
		// sinon on passe à la balise suivante
		if(!nextBalises.empty() && (find(nextBalises.begin(), nextBalises.end(), *itBalises) == nextBalises.end()))
		{
			cerr << "La balise " << *itBalises << " n'est pas au bon endroit." << endl;
			return false;
		}
		else
		{
			nextBalises.clear();
			vector< DTDRule * >::iterator it = findSimpleRule(*itBalises);
			if(it != this->rules.end())
			{
				bool exit = false;
				Tcardinality cardinality = (*it)->getCardinality();
				if(cardinality == CARD_PLUS || cardinality == CARD_STAR)
				{
					DTDSimpleRule* sR = dynamic_cast<DTDSimpleRule*>(*it);
					if(sR != NULL)
					{
						nextBalises.push_back(sR->getName());
					}
				}
				it++;
				while(!exit && it != this->rules.end())
				{
					DTDComplexRule* complexRule;
					DTDSimpleRule* simpleRule = dynamic_cast<DTDSimpleRule*>(*it);
					if(simpleRule != NULL)
					{
						nextBalises.push_back(simpleRule->getName());
						Tcardinality card = simpleRule->getCardinality();
						if(typeActuel == TYPE_COMMA && (card == CARD_NONE || card == CARD_PLUS))
						{
							exit = true;
						}
					}
					else
					{
						complexRule = (DTDComplexRule*) *it;
						complexRule->findEnabledBalises(nextBalises);
					}
					it++;
				}
			}
			else
			{
				it = findComplexRule(*itBalises);
				if(it != this->rules.end())
				{
					DTDComplexRule* complexRule = dynamic_cast<DTDComplexRule*>(*it);
					if(complexRule != NULL)
					{
						complexRule->findEnabledBalises(nextBalises, *itBalises);
					}
				}
				else
				{
					cerr << "La balise " << *itBalises << " n'a pas été trouvée dans la liste des règles." << endl;
					return false;
				}
			}
		}
	}
	return true;
}

// tostring : "(" + toString()  du premier élément + joinType + etc... + ")" + cardinality
string DTDComplexRule::toString()
{
	string s = "(";
	string sType = getStringFromType(joinType);
	vector<DTDRule *>::iterator it = rules.begin();

	for (;;)
	{
		s += (*it)->toString();
		it++;

		if (it != rules.end())
		{
			s+= sType;
		}
		else
		{
			s += ")";
			s += getStringFromCardinality(cardinality);
			break;
		}
	}

	return s;
}

DTDSimpleRule* DTDComplexRule::FindRule(string ruleName)
{
	vector<DTDRule *>::iterator it=rules.begin();
	DTDComplexRule* complexRule;	// Règles testées
	DTDSimpleRule* simpleRule;	// Règles testées

	while(it != rules.end())
	{
		complexRule = dynamic_cast<DTDComplexRule*>(*it); //On teste si on a une règle simple ou complexe

		if(complexRule==NULL)	// Simple règle
		{
			simpleRule = (DTDSimpleRule*) *it;
			if(simpleRule->getName() == ruleName)
			{
				return simpleRule;
			}
		}
		else	// Règle complexe
		{
			simpleRule = complexRule->FindRule(ruleName);
			if(simpleRule != NULL)
			{
				return simpleRule;
			}
		}

		it++;
	}

	return (DTDSimpleRule*) NULL; // Rien trouvé
}


vector< DTDRule* >::iterator DTDComplexRule::findSimpleRule(string ruleName)
{
	vector< DTDRule* >::iterator it;
	for(it = this->rules.begin(); it != this->rules.end(); it++)
	{
		DTDSimpleRule* simpleRule = dynamic_cast<DTDSimpleRule*>(*it);
		if(simpleRule != NULL)
		{
			if(simpleRule->getName().compare(ruleName) == 0)
			{
				return it;
			}
		}
	}
	return this->rules.end();
}

vector< DTDRule* >::iterator DTDComplexRule::findComplexRule(string ruleName)
{
	vector< DTDRule* >::iterator it;
	for(it = this->rules.begin(); it != this->rules.end(); it++)
	{
		DTDComplexRule* complexRule = dynamic_cast<DTDComplexRule*>(*it);
		if(complexRule != NULL)
		{
			vector< DTDRule* > vec = complexRule->rules;
			vector< DTDRule* >::iterator itRule;
			for(itRule = complexRule->rules.begin(); itRule != complexRule->rules.end(); itRule++)
			{
				DTDSimpleRule* simpleRule = dynamic_cast<DTDSimpleRule*>(*itRule);
				if(simpleRule != NULL)
				{
					if(simpleRule->getName().compare(ruleName) == 0)
					{
						return it;
					}
				}
				else
				{
					DTDComplexRule* complexRule = (DTDComplexRule*) *itRule;
					vector< DTDRule* >::iterator i = complexRule->findComplexRule(ruleName);
					if(i != complexRule->rules.end())
					{
						return it;
					}
				}
			}
		}
	}
	return this->rules.end();
}

void DTDComplexRule::findEnabledBalises(vector< string > & enabledBalises)
{
	vector< DTDRule* >::iterator it;
	for(it = this->rules.begin(); it != this->rules.end(); it++)
	{
		DTDSimpleRule* simpleRule = dynamic_cast<DTDSimpleRule*>(*it);
		if(simpleRule == NULL)
		{	//Si complexRule
			DTDComplexRule* complexRule = (DTDComplexRule*) *it;
			complexRule->findEnabledBalises(enabledBalises);
		}
		else
		{	//Si simpleRule
			enabledBalises.push_back(simpleRule->getName());
		}

		if(joinType != TYPE_PIPE)	// Si pas PIPE, pas d'autres balises possibles
		{
			break;
		}
	}
}

void DTDComplexRule::findEnabledBalises(vector< string > & enabledBalises, string balise)
{
	vector< DTDRule* >::iterator it = this->rules.begin();
	bool founded = false;

	while(it != this->rules.end())
	{
		DTDSimpleRule* simpleRule = dynamic_cast<DTDSimpleRule*>(*it);
		if(simpleRule == NULL)
		{	//Si complexRule
			DTDComplexRule* complexRule = (DTDComplexRule*) *it;

			if(founded)
			{
				// Si la balise est déjà trouvée, on peut ajouter les balises suivantes directement
				complexRule->findEnabledBalises(enabledBalises);
			}
			else
			{
				// Si la balise n'est pas déjà trouvée, on doit la trouver avant de pouvoir ajouter
				int size = enabledBalises.size();
				findEnabledBalises(enabledBalises, balise);

				if(size < enabledBalises.size())	// Si la balise a été trouvée, on change l'état.
				{
					founded = true;
				}
			}
		}
		else
		{	//Si simpleRule
			if(founded || (simpleRule->getName() == balise))
			{
				enabledBalises.push_back(simpleRule->getName());
			}
		}

		if( founded && (joinType != TYPE_PIPE))	// Si pas PIPE, pas d'autres balises possibles
		{
			break;
		}

		it++;
	}
}
